﻿/*!	@file	r_gui_widget.cpp
	@brief	GUI
Copyright (c) 2010 Yuya Yamaguchi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "r_gui_widget.h"
#include "r_gui_server.h"
#include "r_gui_allocator.h"
#include <algorithm>

// >>>>>>>>>> RTTI >>>>>>>>>>
#include "../rtti/r_rtti_reflection_implement.h"
namespace r{
	namespace gui{
RRTTI_REFLECTION_IMPLEMENT ( r::gui::Widget, r::rtti::ClassBase,
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( name ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( minWidth, _RST ( "横幅" ) ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( minHeight, _RST ( "縦幅" ) ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( posX, _RST ( "位置X" ) ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( posY, _RST ( "縦幅Y" ) ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( widgetAutoLayout, _RST ( "自動配置" ) ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( parent ),
							 RRTTI_REFLECTION_IMPLEMENT_TYPE ( child ),
						   )
// <<<<<<<<<< RTTI <<<<<<<<<<

RALLOC_PLACEMENT_NEW_DELETE_IMPLEMENT( Widget, r::gui::AllocatorData()() )

		Widget :: Widget()
			:
			minWidth ( 0 ),
			minHeight ( 0 ),
			posX ( 0 ),
			posY ( 0 ),
			posW ( 0 ),
			posH ( 0 ),
			posOffL ( 0 ),
			posOffR ( 0 ),
			posOffT ( 0 ),
			posOffB ( 0 ),
			parent ( NULL ),
			visible ( eRGUIVisible_Show ),
			isHover ( false ),
			widgetAutoLayout ( eRGUIAutoLayout_Virtical )
		{
		}

		Widget :: ~Widget()
		{
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				delete ( *it );
			}
		}

		void	Widget :: update ( Event& guiEvent )
		{
			if ( parent == NULL ) {
				updateWorldPosition ( 0, 0 );
			}
			// 子の更新を行う
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				( *it )->update ( guiEvent );
			}
			if ( guiEvent.getKeyboardFocusWidget() == this ) {
				if ( guiEvent.getOldKeyboardFocusWidget() != this ) {
					onGetKeyboardFocus ( guiEvent );
				}
				onUpdateKeyboardFocus ( guiEvent );
			} else {
				if ( guiEvent.getOldKeyboardFocusWidget() == this ) {
					onLostKeyboardFocus ( guiEvent );
				}
			}
			onUpdate ( guiEvent );
		}

		r_bool	Widget :: checkInRect ( r_ui32 x, r_ui32 y ) const
		{
			r_si32 maxX = posW + wposX;
			r_si32 maxY = posH + wposY;
			if ( ( wposX < static_cast<r_si32> ( x ) ) && ( static_cast<r_si32> ( x ) < maxX ) && ( wposY < static_cast<r_si32> ( y ) ) && ( static_cast<r_si32> ( y ) < maxY ) ) {
				return true;
			}
			return false;
		}

		void	Widget :: updateCkickInRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBClick() != true ) {
				return;
			}
			if ( guiEvent.getLMBDragWidget() != NULL ) {
				return;
			}
			// 子の階層から調査を行う
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) == true ) {
				if ( visible != eRGUIVisible_Close ) {
					for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
						( *it )->updateCkickInRectLMB ( guiEvent );
						if ( guiEvent.getLMBDragWidget() != NULL ) {
							return;
						}
					}
				}
				onClickInRectLMB ( guiEvent );
			}
		}

		void	Widget :: updateDoubleCkickInRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBDClick() != true ) {
				return;
			}
			if ( guiEvent.getLMBDragWidget() != NULL ) {
				return;
			}
			// 子の階層から調査を行う
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) == true ) {
				if ( visible != eRGUIVisible_Close ) {
					for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
						( *it )->updateDoubleCkickInRectLMB ( guiEvent );
						if ( guiEvent.getLMBDragWidget() != NULL ) {
							return;
						}
					}
				}
				onDoubleClickInRectLMB ( guiEvent );
			}
		}

		void	Widget :: updateReleaseInRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBRelease() != true ) {
				return;
			}
			// 直接登録インスタンスを呼び出さずに現在のGUIツリーを辿ることで削除済みデータへのアクセスガード
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) == true ) {
				if ( visible != eRGUIVisible_Close ) {
					for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
						( *it )->updateReleaseInRectLMB ( guiEvent );
					}
				}
				if ( guiEvent.getLMBOldDragWidget() == this ) {
					onReleaseInRectLMB ( guiEvent );
				}
			}
		}

		void	Widget :: updateReleaseOutRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBRelease() != true ) {
				return;
			}
			if ( visible != eRGUIVisible_Close ) {
				for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
					( *it )->updateReleaseOutRectLMB ( guiEvent );
				}
			}
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) != true ) {
				if ( guiEvent.getLMBOldDragWidget() == this ) {
					onReleaseOutRectLMB ( guiEvent );
				}
			}
		}

		void	Widget :: updateDragLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBDragWidget() == NULL ) {
				return;
			}
			if ( guiEvent.getLMBPress() != true ) {
				return;
			}
			if ( visible != eRGUIVisible_Close ) {
				for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
					( *it )->updateDragLMB ( guiEvent );
				}
			}
			if ( guiEvent.getLMBDragWidget() == this ) {
				onDragLMB ( guiEvent );
			}
		}

		void	Widget :: updateDragHoverInRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBDropWidget() == NULL ) {
				return;
			}
			if ( guiEvent.getLMBPress() != true ) {
				return;
			}
			if ( visible != eRGUIVisible_Close ) {
				for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
					( *it )->updateDragHoverInRectLMB ( guiEvent );
				}
			}
			if ( guiEvent.getLMBDragWidget() != this ) {
				onDragHoverInRectLMB ( guiEvent );
			}
		}

		void	Widget :: updateDragReleaseInRectLMB ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBRelease() != true ) {
				return;
			}
			if ( guiEvent.getLMBDropWidget() == NULL ) {
				return;
			}
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) == true ) {
				if ( visible != eRGUIVisible_Close ) {
					for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
						( *it )->updateDragReleaseInRectLMB ( guiEvent );
						if ( guiEvent.getLMBDropWidget() == NULL ) {
							return;
						}
					}
				}
				if ( guiEvent.getLMBOldDragWidget() == this ) {
					onDragReleaseInRectLMB ( guiEvent );
				}
			}
		}

		void	Widget :: updateHoverInRect ( Event& guiEvent )
		{
			if ( ( visible == eRGUIVisible_Hide ) || ( visible == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( ( guiEvent.getLMBHover() != true ) || ( guiEvent.getLMBRelease() == true ) ) {
				return;
			}
			if ( ( guiEvent.getRMBHover() != true ) || ( guiEvent.getRMBRelease() == true ) ) {
				return;
			}
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( checkInRect ( x, y ) == true ) {
				for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
					( *it )->updateHoverInRect ( guiEvent );
				}
				isHover			= true;
				onHoverInRect ( guiEvent );
			}
		}

		void	Widget :: begin ( Event& guiEvent )
		{
			isHover		= false;
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				( *it )->begin ( guiEvent );
			}
		}

		void	Widget :: updateWorldPosition ( r_ui32 wx, r_ui32 wy )
		{
			if ( visible == eRGUIVisible_Hide ) {
				return;
			}
			if ( parent != NULL ) {
				wposX	= wx + posX;
				wposY	= wy + posY;
			} else {
				wposX = posX;
				wposY = posY;
			}
			r_ui32	diffx	= wposX + posOffL;
			r_ui32	diffy	= wposY + posOffT;
		// オートリサイズは封印
		#if 0
			if ( !child.empty() ) {
				posW = 0;
				posH = 0;
			}
		#endif
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				if ( ( visible == eRGUIVisible_Show ) || ( visible == eRGUIVisible_Ghost ) ) {
					( *it )->updateWorldPosition ( diffx, diffy );
					if ( widgetAutoLayout == eRGUIAutoLayout_Virtical ) {
						diffy = ( *it )->wposY + ( *it )->posH + posOffT;
					} else if ( widgetAutoLayout == eRGUIAutoLayout_Holizon ) {
						diffx = ( *it )->wposX + ( *it )->posW + posOffL;
					}
		// オートリサイズは封印
		#if 0

					posW = ( *it )->wposX + ( *it )->posW + posOffX;
					posH = ( *it )->wposY + ( *it )->posH + posOffY;
		#endif
				}
			}
		// 最小サイズはキープ
			if ( posW < minWidth ) {
				posW = minWidth;
			}
			if ( posH < minHeight ) {
				posH = minHeight;
			}
		}

		void	Widget :: draw ( draw::Draw& guidraw )
		{
			// まずは自分自身を描画して
			onDraw ( guidraw );
			// 子の描画を行う
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				( *it )->draw ( guidraw );
			}
		}

		void	Widget :: destroy ( Event& guiEvent )
		{
			for ( Widget_array::iterator	it = child.begin(); it != child.end(); ++it ) {
				( *it )->destroy ( guiEvent );
			}
			if ( guiEvent.getKeyboardFocusWidget() == this ) {
				guiEvent.setKeyboardFocus ( NULL );
			}
		}


		void	Widget :: onUpdate ( Event& guiEvent )
		{
		}

		void	Widget :: onDraw ( draw::Draw& guidraw )
		{
		}

		void	Widget :: onClickInRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onDoubleClickInRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onReleaseInRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onReleaseOutRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onDragLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onDragHoverInRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onDragReleaseInRectLMB ( Event& guiEvent )
		{
		}

		void	Widget :: onHoverInRect ( Event& guiEvent )
		{
		}

		void	Widget :: onGetKeyboardFocus ( Event& guiEvent )
		{
		}

		void	Widget :: onLostKeyboardFocus ( Event& guiEvent )
		{
		}

		void	Widget :: onUpdateKeyboardFocus ( Event& guiEvent )
		{
		}

		void	Widget :: getPos ( r_ui32& x, r_ui32& y ) const
		{
			x	= posX;
			y	= posY;
		}

		void	Widget :: getSize ( r_ui32& w, r_ui32& h ) const
		{
			w	= posW;
			h	= posH;
		}

		void	Widget :: setPos ( r_ui32 x, r_ui32 y )
		{
			posX	= x;
			posY	= y;
		}

		void	Widget :: setSize ( r_ui32 w, r_ui32 h )
		{
			posW	= w;
			posH	= h;
		}

		void	Widget :: setName ( const r_cstr text )
		{
			name	= text;
		}

		const r_cstr	Widget :: getName() const
		{
			return name.c_str();
		}

		r_size	Widget :: getNameLength() const
		{
			return	name.length();
		}

		void	Widget :: setVisible ( r_si32 mode )
		{
			visible		= mode;
		}

		r_si32	Widget :: getVisible() const
		{
			return visible;
		}

		r_bool	Widget :: getIsHover() const
		{
			return isHover;
		}

		void	Widget :: setAutoLaytou ( r_ui32 layout )
		{
			widgetAutoLayout	= layout;
		}

		void	Widget :: addChild ( Widget* widget )
		{
			widget->parent = this;
			child.push_back ( widget );
		}

		Widget*	Widget :: getParent() const
		{
			return parent;
		}

		r_size	Widget :: getChildNum() const
		{
			return child.size();
		}

		Widget*	Widget :: getChild ( r_size index ) const
		{
			return child[index];
		}

		void	Widget :: remove()
		{
			if ( parent == NULL ) {
				return;
			}
			// 親から自分を削除
			Widget_array::iterator	it =
				std::find ( parent->child.begin(), parent->child.end(), this );
			// 親には登録がない
			if ( it == parent->child.end() ) {
				return;
			}
			// 親から削除を行う
			parent->child.erase ( it );
		}
	}
}


